package com.jidolstar.astro
{
	/**
	 * Nutation(장동)
	 * @author Yongho,Ji (jidolstar@gmail.com)
	 * @since 2009.04.09
	 * @see 
	 * http://en.wikipedia.org/wiki/Nutation
	 * Astronomy on the personal computer 4th 6.4.2 Nutation page. 123
	 * Astronomical Algorithms 2nd Chanpter 22 page.143
	 *  
	 * @example
	 * <listing version="3.0">
	 * var JDE:Number = 2446895.5;
	 * var nutation:ALNutation = new ALNutation( JDE, ALNutation.HIGH_ACCURACY, ALNutation.LONG_PERIOD );
	 * trace( "JDE: " + nutation.JDE ); //JDE: 2446895.5
	 * trace( "accurcy: " + nutation.accuracy ); //accurcy: highAccuracy
	 * trace( "period: " + nutation.period ); //period: longPeriod
	 * trace( "Δψ : " + nutation.nutationInLongitude * ALConst.R2S); //Δψ : -3.787917163078442
	 * trace( "Δε : " + nutation.nutationInObliquity * ALConst.R2S); //Δε : 9.442520698757397
	 * trace( "ε0 : " + ALDMS.fractionalDegree2DMS( nutation.meanObliquityOfEcliptic * ALConst.R2D ).toString() ); //ε0 : 23° 26’ 27”.37
	 * trace( "ε  : " + ALDMS.fractionalDegree2DMS( nutation.trueObliquityOfEcliptic * ALConst.R2D ).toString() ); //ε  : 23° 26’ 36”.81
	 * 
	 * //JDE는 같지만 적용기간을 길게 잡는다. 이는 오랜 시간이 지나도 오차를 적게내도록 한다.
	 * nutation.accuracy = ALNutation.HIGH_ACCURACY;
	 * nutation.period = ALNutation.SHORT_PERIOD;
	 * trace( "JDE: " + nutation.JDE ); //JDE: 2446895.5
	 * trace( "accurcy: " + nutation.accuracy ); //accurcy: highAccuracy
	 * trace( "peroid: " + nutation.period ); //peroid: shortPeriod
	 * trace( "Δψ : " + nutation.nutationInLongitude * ALConst.R2S); //Δψ : -3.787917163078442
	 * trace( "Δε : " + nutation.nutationInObliquity * ALConst.R2S); //Δε : 9.442520698757397
	 * trace( "ε0 : " + ALDMS.fractionalDegree2DMS( nutation.meanObliquityOfEcliptic * ALConst.R2D ).toString() ); //ε0 : 23° 26’ 27”.41
	 * trace( "ε  : " + ALDMS.fractionalDegree2DMS( nutation.trueObliquityOfEcliptic * ALConst.R2D ).toString() ); //ε  : 23° 26’ 36”.85
	 * 
	 * //2028년 11월 13.19일 UT1
	 * var date:ALDate = new ALDate( 2028, 11, 13.19 );
	 * var deltaT:Number = ALDynamicalTime.deltaT( date.fractionalYear );
	 * JDE = date.JD + deltaT / 3600;
	 * nutation.JDE = date.JD;
	 * trace( "JDE: " + nutation.JDE ); //JDE: 2462088.689583333
	 * trace( "accurcy: " + nutation.accuracy ); //accurcy: highAccuracy
	 * trace( "peroid: " + nutation.period ); //peroid: shortPeriod
	 * trace( "Δψ : " + nutation.nutationInLongitude * ALConst.R2S); //Δψ : 14.860843903561848
	 * trace( "Δε : " + nutation.nutationInLongitude * ALConst.R2S); //Δε : 2.7046046954480025
	 * trace( "ε0 : " + ALDMS.fractionalDegree2DMS( nutation.meanObliquityOfEcliptic * ALConst.R2D ).toString() ); //ε0 : 23° 26’ 07”.93
	 * trace( "ε  : " + ALDMS.fractionalDegree2DMS( nutation.trueObliquityOfEcliptic * ALConst.R2D ).toString() ); //ε  : 23° 26’ 10”.64 
	 * 
	 * //Nutation이 적용된 Matrix를 받아와 별의 적경(2h24m11.331s=41°.5472),적위(+49°20'54".54=+49°.3485)에 적용 
	 * //Astronomical Algorithms 2nd p.153
	 * var equNutationMatrix:ALMatrix3D = nutation.getNutationOfEquMatrix();
	 * var equVector:ALVector3D = new ALVector3D( 41.5472 * ALConst.D2R, 49.3485 * ALConst.D2R, 1, ALVector3D.COORDINATE_TYPE_SPHERICAL );
	 * var equNutationVectorArray:Array = equNutationMatrix.multiply3x3ByVector3D( [equVector] );
	 * var equNutationVector:ALVector3D = equNutationVectorArray[0];
	 * trace( "RA : " + ALHMS.fractionalHour2HMS( equNutationVector.longitudeHour ).toString("h","m","s") ); //RA : 02h46m12.38s
	 * trace( "DE : " + ALDMS.fractionalDegree2DMS( equNutationVector.latitudeDegree ).toString() ); //DE : 49° 21’ 00”.82
	 * trace( "dRA : " + (equNutationVector.longitude - RA) * ALConst.R2S ); //dRA : 15.843198038325333
	 * trace( "dDE : " + (equNutationVector.latitude - DE) * ALConst.R2S ); //dDE : 6.217170560031128
 	 * </listing>
	 */ 
	public class ALNutation
	{
		static public const SHORT_PERIOD:String = "shortPeriod";
		static public const LONG_PERIOD:String ="longPeriod";
		static public const HIGH_ACCURACY:String = "highAccuracy";
		static public const LOW_ACCURACY:String = "lowAccuracy";
		 
		private var _accuracy:String;
		private var _period:String; 
		 
		private var _JDE:Number; //JDE = JD + deltaT / 86400;
		private var T:Number; // Julian centuries from 2000 January 1.5, barycentric dynamical time
		private var T2:Number; // T^2
		private var T3:Number; // T^3
		
		private var _O:Number; //Ω 황도상에서 달의 평균 궤도의 승교점의 경도(Logitude of the ascending node of the Moon's mean obit on the ecliptic)
		private var _L:Number; //L : 태양의 평균 황경(the mean logitude of the sun)
		private var _LL:Number; //L': 달의 평균 황경(the mean logitude of the moon)
		private var _D:Number; //D : 태양으로부터 달까의 평균이각(mean elongation of the moon from the sun)
		private var _M:Number; //M : 태양(지구)의 평균근점이각(mean anomaly of the sun)
		private var _MM:Number; //M' : 달의 평균 근점이각(mean anomaly of the moon)
		private var _F:Number; //F : Moon's argument of latitude
		private var _nutationInLongitude:Number; //Δψ : nutation in longitude(황도상에서 장동운동에 의한 황경 변화값)
		private var _nutationInObliquity:Number; //Δε : the nutation in obliquity(황도상에서 장동운동에 의한 기울기 변화값)
		private var _meanObliquityOfEcliptic:Number; //ε0 : the mean obliquity of the ecliptic(적도와 황도의 기울기 평균값)
		
		private var isSetO:Boolean = false;
		private var isSetL:Boolean = false;
		private var isSetLL:Boolean = false;
		private var isSetD:Boolean = false;
		private var isSetM:Boolean =false;
		private var isSetMM:Boolean = false;
		private var isSetF:Boolean = false;
		private var isSetMeanObliquityOfEcliptic:Boolean = false;
		private var isSetLongitudeAndObliquity:Boolean =false;
		
		static private var nutationArguments:Array = null;
		static private var nutationCoefficients:Array = null; 
		
		/**
		 * 장동운동 생성자
		 * @param JDE Julian Ephemeris Date(JDE). JDE = JD + deltaT / 86400
		 * @param accuracy 
		 * @param period
		 * 
		 */ 
		public function ALNutation( JDE:Number, accuracy:String = "highAccuracy", period:String = "longPeriod" )
		{
			this.JDE = JDE;
			this.accuracy = accuracy;
			this.period = period
		}
		
		/**
		 * Julian Ephemeris Date(JDE). JDE = JD + deltaT / 86400;
		 */ 
		public function get JDE():Number
		{
			return _JDE;
		}
		
		/**
		 * @private
		 */ 
		public function set JDE( value:Number ):void
		{
			this._JDE = value;
			this.T = ALTimeConverter.getJulianCenturyJ2000( value );
			this.T2 = T*T;
			this.T3 = T*T2;	
			isSetLongitudeAndObliquity = false;
			isSetMeanObliquityOfEcliptic = false;
			isSetO = false;
			isSetLL = false;
			isSetL = false;
			isSetD = false;
			isSetM = false;
			isSetMM = false;
			isSetF = false;					
			isSetLongitudeAndObliquity = false;
		}
		
		/**
		 * 정확도. ALNutation.HIGH_ACCURACY 또는 ALNutation.LOW_ACCURACY 설정 
		 */ 
		public function get accuracy():String
		{
			return _accuracy;
		}
		
		/**
		 * @private
		 */ 
		public function set accuracy( value:String ):void
		{
			if( _accuracy == value ) return; 	
			isSetLongitudeAndObliquity = false;
			isSetMeanObliquityOfEcliptic = false;
			isSetO = false;
			isSetLL = false;
			isSetL = false;
			isSetD = false;
			isSetM = false;
			isSetMM = false;
			isSetF = false;
			_accuracy = value == HIGH_ACCURACY ? HIGH_ACCURACY : LOW_ACCURACY;
		}
		
		/**
		 * 적용기잔. ALNutation.LONG_PERIOD 또는 ALNutation.SHORT_PERIOD 설정 
		 */ 
		public function get period():String
		{
			return _period;
		}
				
		/**
		 * @private
		 */ 
		public function set period( value:String ):void
		{
			if( _period == value ) return; 	
			isSetLongitudeAndObliquity = false;
			isSetO = false;
			isSetLL = false;
			isSetL = false;
			isSetD = false;
			isSetM = false;
			isSetMM = false;
			isSetF = false;
			_period = value == LONG_PERIOD ? LONG_PERIOD : SHORT_PERIOD;
		}
		
		/**
		 * ε0 : the mean obliquity of the ecliptic(적도와 황도의 기울기 평균값)<br>
		 * 단위 radian
		 * 
		 * @return 
		 */ 
		public function get meanObliquityOfEcliptic():Number
		{
			if( isSetMeanObliquityOfEcliptic ) return _meanObliquityOfEcliptic;
			switch( _period )
			{
				//Astronomical Algorithms p147
				//Astronomy on the Personal Computer p19
				//2000년에 1" 오차, 4000년에 10" 오차가 발생한다. 
				case SHORT_PERIOD:
					_meanObliquityOfEcliptic = ( 23.43929111 - (46.8150 + (0.00059 - 0.001813 * T) * T) * T/3600 ) * ALConst.D2R;
					break;

				//Astronomical Algorithms p147.
				//Note the official 1976 IAU number is 23d 26' 21.448", but
 				//the JPL numerical integration found 21.4119".
 				//
				//AD 1000~3000 년에 0.01 arcsec 오차, 10000년도 몇 arcsec 정도 오차를 가짐.
				//이것은 10000년까지만 사용할 수 있음  
				case LONG_PERIOD:
					var t:Number = this.T / 10;
					_meanObliquityOfEcliptic = 
								(((((((((( 2.45e-10*t + 5.79e-9)*t + 2.787e-7)*t
        						+ 7.12e-7)*t - 3.905e-5)*t - 2.4967e-3)*t
								- 5.138e-3)*t + 1.9989)*t - 0.0152)*t - 468.0927)*t
								+ 84381.412) * ALConst.S2R;
					break;
			}
			return _meanObliquityOfEcliptic;
		}
		
		/**
		 * ε : the true obliquity of the ecliptic(적도와 황도의 기울기 실제값)<br> 
		 * ε = ε0 + Δε
		 */ 
		public function get trueObliquityOfEcliptic():Number
		{
			return meanObliquityOfEcliptic + nutationInObliquity ;
		}
		
		/**
		 * Δε : the nutation in obliquity(황도상에서 장동운동에 의한 기울기 변화값)
		 * 
		 * <p>
		 * High accuracy<br> 
		 *  use 1980 IAU Theory of Nutation <br>
		 * Low accuracy : 0.1 arcsecond 정확도<br> 
		 *  use Δε = +9".20 cos Ω + 0".57 cos 2L + 0".10 cos 2L' - 0".09 cos 2Ω
		 * </p>
		 */ 
		public function get nutationInObliquity():Number
		{
			calculateObliquityAndLongitude();
			return _nutationInObliquity;	

		}
		
		/**
		 * Δψ : nutation in longitude(황도상에서 장동운동에 의한 황경 변화값)
		 * 
		 * <p>
		 * High accuracy<br> 
		 * 	 use 1980 IAU Theory of Nutation<br>
		 * Low accuracy : 0.5 arcsecond 정확도<br> 
		 *   use Δψ = -17".20 sin Ω - 1".32 sin 2L - 0".23 sin 2L' + 0".21 sin 2Ω<br>
		 * </p>
		 */ 
		public function get nutationInLongitude():Number
		{
			calculateObliquityAndLongitude();
			return _nutationInLongitude;	
		}
		
		/**
		 * @private
		 * Δψ, Δε를 계산한다.
		 */ 
		private function calculateObliquityAndLongitude():void
		{
			if( isSetLongitudeAndObliquity ) return;
			isSetLongitudeAndObliquity = true;
			var O:Number;
			
			switch( _accuracy )
			{
				case HIGH_ACCURACY:
					setNutationArgumentAndCoefficient();
					O = getO();
					var D:Number = getD();
					var M:Number = getM();
					var MM:Number = getMM();
					var F:Number = getF();
					
					var terms:int = nutationCoefficients.length;
					var coeff_sin:Number;
					var coeff_cos:Number;
					var coeff:NutationCoefficient;
					var argu:NutationArgument;
					var i:int;
					var temp:Number;
					
					_nutationInLongitude = 0;
					_nutationInObliquity = 0;
					
					for( i=0; i < terms; i++ )
					{
						// calculate nutationCoefficients of sine and cosine
						coeff = nutationCoefficients[i];
						coeff_sin = (coeff.longitude1 + (coeff.longitude2 * T));
						coeff_cos = (coeff.obliquity1 + (coeff.obliquity2 * T));
						
						// sum the nutationArguments 
						argu = nutationArguments[i];
						temp = argu.D * D + argu.M * M + argu.MM * MM + argu.F * F + argu.O * O;
						temp *= ALConst.D2R;
						_nutationInLongitude += coeff_sin * Math.sin( temp );
						_nutationInObliquity += coeff_cos * Math.cos( temp );							
					}
					
					// change to arcsecs
					_nutationInLongitude *= 0.0001;
					_nutationInObliquity *= 0.0001;
					
					// change to radian
					_nutationInLongitude *= ALConst.S2R;
					_nutationInObliquity *= ALConst.S2R;					
					break;
					
				case LOW_ACCURACY:
					O = getO() * ALConst.D2R;
					
					var L:Number = getL() * ALConst.D2R;
					var LL:Number = getLL() * ALConst.D2R;
					
					_nutationInLongitude = -17.20 * Math.sin( O )- 1.32 * Math.sin( 2 * L ) - 0.23 * Math.sin( 2 * LL ) + 0.21 * Math.sin( 2 * O );
					_nutationInObliquity = 9.20 * Math.cos( O ) + 0.57 * Math.cos( 2 * L ) + 0.10 * Math.cos( 2 * LL ) - 0.09 * Math.cos( 2 * O );
					
					// change to radian
					_nutationInLongitude = _nutationInLongitude * ALConst.S2R;
					_nutationInObliquity = _nutationInObliquity * ALConst.S2R;
					break;
			}				
		}
		
		/**
		 * Nutation of Equatorial Coordinate Transformation Matrix<br>
		 * nutation in longitude(Δψ)과 obliquity and obliquity(Δε)는 황도좌표계에서 계산된다.
		 * 계산된 값은 적도좌표계로 변환하기 위한 행렬을 만들기 위해 쓰인다. 
		 * 만들어진 행렬은 3번 회전한 건데 먼저 주어진 날짜로 X축 회전, Δψ를 적용한 Z축 회전, Δε를 적용한 x축 회전이다.
		 * 
		 * @see
		 * Astronomy on the personal computer 4th p.123
		 * Astronomical Algorithms 2nd p.151
		 * Astronomical Almanic page B20
		 */ 
		public function getNutationOfEquMatrix():ALMatrix3D
		{
			var o:Number = nutationInObliquity; //Δε
			var l:Number = nutationInLongitude; //Δψ 
			var e0:Number = meanObliquityOfEcliptic; //ε0
			var e:Number = trueObliquityOfEcliptic; //ε = ε0 + Δε
			var cos_e0:Number = Math.cos( e0 );
			var sin_e0:Number = Math.sin( e0 );			
			var cos_e:Number = Math.cos( e );
			var sin_e:Number = Math.sin( e );
			var sin_o:Number = Math.sin( o );
			var cos_l:Number = Math.cos( l );
			var sin_l:Number = Math.sin( l );
			 
			var matrix:ALMatrix3D = 
				new ALMatrix3D( cos_l, 			
								-sin_l * cos_e0, 
								-sin_l * sin_e0, 
								sin_l * cos_e,  
								cos_l * cos_e0 * cos_e + sin_e0 * sin_e,
								-(sin_o + (1.0-cos_l) * sin_e0 * cos_e),
								sin_l * sin_e, 	
								sin_o + (cos_l-1.0) * sin_e * cos_e0,  	
								cos_l * sin_e0 * sin_e + cos_e0 * cos_e);
			return matrix;
		}

		/**
		 * L : 태양의 평균 황경(the mean logitude of the sun)<br>
		 * L = 280°.4665 + 36000°.7698 T
		 */ 
		private function getL():Number
		{
			if( !isSetL )
			{
				isSetL = false;
				_L = ALMath.mapTo0To360Range( 280.4665 + 36000.7698 * T );
			}
			return _L;
		}
		
		/**
		 * L': 달의 평균 황경(the mean logitude of the moon)
		 * L' = 218°.3165 + 481267°.8813 T<br>
		 */ 
		private function getLL():Number
		{
			if( !isSetLL )
			{
				isSetLL = false;
				_LL = ALMath.mapTo0To360Range( 218.3165 + 481267.8813 * T );
			}
			return _LL;
		}

		/**
		 * Ω : 황도상에서 달의 평균 궤도의 승교점의 경도(Logitude of the ascending node of the Moon's mean obit on the ecliptic)<br>
		 * Ω = 125°.04452 - 1934.136261 T + 0.0020708 T<sup>2</sup> + T<sup>3</sup> / 450000<br>
		 */ 
		private function getO():Number
		{
			if( !isSetO )
			{
				isSetO = false;
				_O = ALMath.mapTo0To360Range( 125.04452 - 1934.136261 * T + 0.0020708 * T2 + T3 / 450000.0);
			}
			return _O;
		}
		
		/**
		 * D : 태양으로부터 달까의 평균이각(mean elongation of the moon from the sun)
		 */ 
		private function getD():Number
		{
			if( !isSetD )
			{
				isSetD = false;
				_D = ALMath.mapTo0To360Range( 297.85036 + 445267.111480 * T - 0.0019142 * T2 + T3 / 189474.0 );
			}
			return _D;
		}	
		
		/**
		 * M : 태양(지구)의 평균근점이각(mean anomaly of the sun)
		 */ 
		private function getM():Number
		{
			if( !isSetM )
			{
				isSetM = false;
				_M = ALMath.mapTo0To360Range( 357.52772 + 35999.050340 * T - 0.0001603 * T2 - T3 / 300000.0 );
			}
			return _M;
		}	
		
		/**
		 * M' : 달의 평균 근점이각(mean anomaly of the moon)
		 */ 
		private function getMM():Number
		{
			if( !isSetMM )
			{
				isSetMM = false;
				_MM = ALMath.mapTo0To360Range( 134.96298 + 477198.867398 * T + 0.0086972 * T2 + T3 / 56250.0 );
			}
			return _MM;
		}	
		
		/**
		 * F : Moon's argument of latitude
		 */ 
		private function getF():Number
		{
			if( !isSetF )
			{
				isSetF = false;
				_F = ALMath.mapTo0To360Range( 93.2719100 + 483202.017538 * T - 0.0036825 * T2 + T3 / 327270.0 );
			}
			return _F;
		}			
		
		/**
		 * Nutation nutationArguments and nutationCoefficients<br>
		 * nutationArguments and nutationCoefficients taken from Astronomical Algorithms 2nd table 22A on page 145
		 */ 
		static private function setNutationArgumentAndCoefficient():void
		{
			if( nutationCoefficients == null )
			{
				nutationCoefficients = [
						new NutationCoefficient(-171996.0,	-174.2,	92025.0,8.9),
					    new NutationCoefficient(-13187.0,	-1.6,  	5736.0,	-3.1),
					    new NutationCoefficient(-2274.0, 	 0.2,  	977.0,	-0.5),
					    new NutationCoefficient(2062.0,   	0.2,    -895.0,    0.5),
					    new NutationCoefficient(1426.0,    -3.4,    54.0,    -0.1),
					    new NutationCoefficient(712.0,    0.1,    -7.0,    0.0),
					    new NutationCoefficient(-517.0,    1.2,    224.0,    -0.6),
					    new NutationCoefficient(-386.0,    -0.4,    200.0,    0.0),
					    new NutationCoefficient(-301.0,    0.0,    129.0,    -0.1),
					    new NutationCoefficient(217.0,    -0.5,    -95.0,    0.3),
					    new NutationCoefficient(-158.0,    0.0,    0.0,    0.0),
					    new NutationCoefficient(129.0,	0.1,	-70.0,	0.0),
					    new NutationCoefficient(123.0,	0.0,	-53.0,	0.0),
					    new NutationCoefficient(63.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(63.0,	1.0,	-33.0,	0.0),
					    new NutationCoefficient(-59.0,	0.0,	26.0,	0.0),
					    new NutationCoefficient(-58.0,	-0.1,	32.0,	0.0),
					    new NutationCoefficient(-51.0,	0.0,	27.0,	0.0),
					    new NutationCoefficient(48.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(46.0,	0.0,	-24.0,	0.0),
					    new NutationCoefficient(-38.0,	0.0,	16.0,	0.0),
					    new NutationCoefficient(-31.0,	0.0,	13.0,	0.0),
					    new NutationCoefficient(29.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(29.0,	0.0,	-12.0,	0.0),
					    new NutationCoefficient(26.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(-22.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(21.0,	0.0,	-10.0,	0.0),
					    new NutationCoefficient(17.0,	-0.1,	0.0,	0.0),
					    new NutationCoefficient(16.0,	0.0,	-8.0,	0.0),
					    new NutationCoefficient(-16.0,	0.1,	7.0,	0.0),
					    new NutationCoefficient(-15.0,	0.0,	9.0,	0.0),
					    new NutationCoefficient(-13.0,	0.0,	7.0,	0.0),
					    new NutationCoefficient(-12.0,	0.0,	6.0,	0.0),
					    new NutationCoefficient(11.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(-10.0,	0.0,	5.0,	0.0),
					    new NutationCoefficient(-8.0,	0.0,	3.0,	0.0),
					    new NutationCoefficient(7.0,	0.0,	-3.0,	0.0),
					    new NutationCoefficient(-7.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(-7.0,	0.0,	3.0,	0.0),
					    new NutationCoefficient(-7.0,	0.0,	3.0,	0.0),
					    new NutationCoefficient(6.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(6.0,	0.0,	-3.0,	0.0),
					    new NutationCoefficient(6.0,	0.0,	-3.0,	0.0),
					    new NutationCoefficient(-6.0,	0.0,	3.0,	0.0),
					    new NutationCoefficient(-6.0,	0.0,	3.0,	0.0),
					    new NutationCoefficient(5.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(-5.0,	0.0,	3.0,	0.0),
					    new NutationCoefficient(-5.0,	0.0,	3.0,	0.0),
					    new NutationCoefficient(-5.0,	0.0,	3.0,	0.0),
					    new NutationCoefficient(4.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(4.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(4.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(-4.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(-4.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(-4.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(3.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(-3.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(-3.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(-3.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(-3.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(-3.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(-3.0,	0.0,	0.0,	0.0),
					    new NutationCoefficient(-3.0,	0.0,	0.0,	0.0)				 
				 ];					
			}
			
			if( nutationArguments == null )
			{
				nutationArguments = [ 
					    new NutationArgument(0.0,	0.0,	0.0,	0.0,	1.0),
					    new NutationArgument(-2.0,	0.0,	0.0,	2.0,	2.0),
					    new NutationArgument(0.0,	0.0,	0.0,	2.0,	2.0),
					    new NutationArgument(0.0,	0.0,	0.0,	0.0,	2.0),
					    new NutationArgument(0.0,	1.0,	0.0,	0.0,	0.0),
					    new NutationArgument(0.0,	0.0,	1.0,	0.0,	0.0),
					    new NutationArgument(-2.0,	1.0,	0.0,	2.0,	2.0),
					    new NutationArgument(0.0,	0.0,	0.0,	2.0,	1.0),
					    new NutationArgument(0.0,	0.0,	1.0,	2.0,	2.0),
					    new NutationArgument(-2.0,	-1.0,	0.0,	2.0,	2.0),
					    new NutationArgument(-2.0,	0.0,	1.0,	0.0,	0.0),
					    new NutationArgument(-2.0,	0.0,	0.0,	2.0,	1.0),
					    new NutationArgument(0.0,	0.0,	-1.0,	2.0,	2.0),
					    new NutationArgument(2.0,	0.0,	0.0,	0.0,	0.0),
					    new NutationArgument(0.0,	0.0,	1.0,	0.0,	1.0),
					    new NutationArgument(2.0,	0.0,	-1.0,	2.0,	2.0),
					    new NutationArgument(0.0,	0.0,	-1.0,	0.0,	1.0),
					    new NutationArgument(0.0,	0.0,	1.0,	2.0,	1.0),
					    new NutationArgument(-2.0,	0.0,	2.0,	0.0,	0.0),
					    new NutationArgument(0.0,	0.0,	-2.0,	2.0,	1.0),
					    new NutationArgument(2.0,	0.0,	0.0,	2.0,	2.0),
					    new NutationArgument(0.0,	0.0,	2.0,	2.0,	2.0),
					    new NutationArgument(0.0,	0.0,	2.0,	0.0,	0.0),
					    new NutationArgument(-2.0,	0.0,	1.0,	2.0,	2.0),
					    new NutationArgument(0.0,	0.0,	0.0,	2.0,	0.0),
					    new NutationArgument(-2.0,	0.0,	0.0,	2.0,	0.0),
					    new NutationArgument(0.0,	0.0,	-1.0,	2.0,	1.0),
					    new NutationArgument(0.0,	2.0,	0.0,	0.0,	0.0),
					    new NutationArgument(2.0,	0.0,	-1.0,	0.0,	1.0),
					    new NutationArgument(-2.0,	2.0,	0.0,	2.0,	2.0),
					    new NutationArgument(0.0,	1.0,	0.0,	0.0,	1.0),
					    new NutationArgument(-2.0,	0.0,	1.0,	0.0,	1.0),
					    new NutationArgument(0.0,	-1.0,	0.0,	0.0,	1.0),
					    new NutationArgument(0.0,	0.0,	2.0,	-2.0,	0.0),
					    new NutationArgument(2.0,	0.0,	-1.0,	2.0,	1.0),
					    new NutationArgument(2.0,	0.0,	1.0,	2.0,	2.0),
					    new NutationArgument(0.0,	1.0,	0.0,	2.0,	2.0),
					    new NutationArgument(-2.0,	1.0,	1.0,	0.0,	0.0),
					    new NutationArgument(0.0,	-1.0,	0.0,	2.0,	2.0),
					    new NutationArgument(2.0,	0.0,	0.0,	2.0,	1.0),
					    new NutationArgument(2.0,	0.0,	1.0,	0.0,	0.0),
					    new NutationArgument(-2.0,	0.0,	2.0,	2.0,	2.0),
					    new NutationArgument(-2.0,	0.0,	1.0,	2.0,	1.0),
					    new NutationArgument(2.0,	0.0,	-2.0,	0.0,	1.0),
					    new NutationArgument(2.0,	0.0,	0.0,	0.0,	1.0),
					    new NutationArgument(0.0,	-1.0,	1.0,	0.0,	0.0),
					    new NutationArgument(-2.0,	-1.0,	0.0,	2.0,	1.0),
					    new NutationArgument(-2.0,	0.0,	0.0,	0.0,	1.0),
					    new NutationArgument(0.0,	0.0,	2.0,	2.0,	1.0),
					    new NutationArgument(-2.0,	0.0,	2.0,	0.0,	1.0),
					    new NutationArgument(-2.0,	1.0,	0.0,	2.0,	1.0),
					    new NutationArgument(0.0,	0.0,	1.0,	-2.0,	0.0),
					    new NutationArgument(-1.0,	0.0,	1.0,	0.0,	0.0),
					    new NutationArgument(-2.0,	1.0,	0.0,	0.0,	0.0),
					    new NutationArgument(1.0,	0.0,	0.0,	0.0,	0.0),
					    new NutationArgument(0.0,	0.0,	1.0,	2.0,	0.0),
					    new NutationArgument(0.0,	0.0,	-2.0,	2.0,	2.0),
					    new NutationArgument(-1.0,	-1.0,	1.0,	0.0,	0.0),
					    new NutationArgument(0.0,	1.0,	1.0,	0.0,	0.0),
					    new NutationArgument(0.0,	-1.0,	1.0,	2.0,	2.0),
					    new NutationArgument(2.0,	-1.0,	-1.0,	2.0,	2.0),
					    new NutationArgument(0.0,	0.0,	3.0,	2.0,	2.0),
					    new NutationArgument(2.0,	-1.0,	0.0,	2.0,	2.0) ];
						
			}		
						
			
		}
	}
}

/**
 * Nutation Coefficient
 */ 
class NutationCoefficient
{
	public var longitude1:Number;
	public var longitude2:Number;
	public var obliquity1:Number;
	public var obliquity2:Number;

	public function NutationCoefficient( longitude1:Number, longitude2:Number, obliquity1:Number, obliquity2:Number  )
	{
		this.longitude1 = longitude1;
		this.longitude2 = longitude2;
		this.obliquity1 = obliquity1;
		this.obliquity2 = obliquity2;
	}
}

/**
 * Nutation Argument
 */ 
class NutationArgument
{
	public var D:int;
	public var M:int;
	public var MM:int;
	public var F:int;
	public var O:int;
	
	public function NutationArgument( D:int, M:int, MM:int, F:int, O:int ) 
	{
		this.D = D;
		this.M = M;
		this.MM = MM;
		this.F = F;
		this.O = O;
	}
}